# -*- mode: org; fill-column: 80; -*-
#+TITLE: Org SBE example
#+SUBTITLE: Examples
#+AUTHOR: Zelphir Kaltstahl
#+STARTUP: content
#+STARTUP: indent
#+STARTUP: align
#+STARTUP: shrink
#+STARTUP: inlineimages
#+STARTUP: entitiesplain
#+STARTUP: nologdone
#+STARTUP: nologreschedule
#+STARTUP: nologredeadline
#+STARTUP: nologrefile
#+STARTUP: hidestars
#+STARTUP: oddeven
#+TODO: TODO WIP | DONE
#+DATE: [2023-03-05 So]
#+LANGUAGE: English
#+PRIORITIES: A C C
#+EXCLUDE_TAGS: noexport
#+KEYWORDS: emacs org-mode spreadsheet sbe meta-programming scheme

* Idea
:PROPERTIES:
:CUSTOM_ID: idea
:END:

This example shows how one can make use of any org-babel supported programming language inside org-mode tables/spreadsheets. This allows the user to use the most suitable programming language for the task at hand to process data and put results back into the spreadsheet.

* Data
:PROPERTIES:
:CUSTOM_ID: data
:END:

Define some data as output string. We can use whatever way we want to generate the data. All that matters is, that the data is output as a result of this source block.

#+name: scheme-data
#+begin_src scheme :results output drawer replace
(display 1) (newline)
(display 2) (newline)
(display 3) (newline)
(display 4) (newline)
(display 5) (newline)
(display 4) (newline)
(display 5) (newline)
(display 4) (newline)
(display 5) (newline)
(display 4) (newline)
(display 5) (newline)
#+end_src

#+RESULTS: scheme-data
:results:
1
2
3
4
5
4
5
4
4
5
:end:

* Scheme value converter
:PROPERTIES:
:CUSTOM_ID: scheme-value-converter
:END:

Input is always a string, when it is given via =:var= in source block header arguments. It needs to be converted to a list first. The following defines a converter function, which splits a string into a list of strings, using any whitespace as a separator. If the input is for example a sloppily written bunch of numbers, they will be converted into a list of strings, each containing one number.

#+name: scheme-converter
#+begin_src scheme :noweb strip-export :results none
(define convert:string->list
  (λ (str)
    (filter (λ (str) (not (string-null? str)))
            (string-split str char-set:whitespace))))
#+end_src

* Business logic function definition
:PROPERTIES:
:CUSTOM_ID: business-logic
:END:

We implement the actual business logic function to process the data. Here we make use of the ~:noweb~ header argument, to source the converter function.

#+name: scheme-mean
#+begin_src scheme :noweb strip-export :results output drawer replace :var x=scheme-data()
<<scheme-converter>>

(define mean
  (λ (lst)
    (let ([len (length lst)])
      (/ (apply + lst) len))))

(define mean-inexact
  (λ (lst)
    (exact->inexact (mean lst))))

(simple-format
 #t "~a\n"
 (mean-inexact
  (map string->number
       (convert:string->list x))))
#+end_src

#+RESULTS: scheme-mean
:results:
2.5
:end:

* Usage in org mode spreadsheet
:PROPERTIES:
:CUSTOM_ID: usage-in-spreadsheet
:END:

Here we make use of ~org-sbe~ in the ~#+TBLFM:~ table formula. We name the source block we want to run and define values of variables for that source block. Important: The source block needs to accept/receive the variable via a ~:var~ header argument. In this case the variable is named =x=. The value of the variable can be retrieved from another named source block. In this case we call the source block =scheme-data= to generate our data.

#+name: summaries
|   |               mean |
|---+--------------------|
| # | 3.8181818181818183 |
#+TBLFM: @2$2='(org-sbe "scheme-mean" (x "scheme-data()"))

|   | col1 | col2 |
|---+------+------|
| # |    1 |    2 |
| # |   10 |   11 |
| # |    3 |    4 |
#+TBLFM: $3=$2 + 1

* Use table as input for a source block
:PROPERTIES:
:CUSTOM_ID: reference-table-data
:END:

Possible syntaxes for referencing tables:

+ ~example-table[0,-1]~
+ ~example-table[1:3]~ (range)
+ ~example-table[,0]~ (all of row or column)
+ ~example-table[1,,1]~ (multiple dimensions, for output of other source blocks, that would be tables or arrays or lists os ...)

  #+begin_quote
  Index referencing can be used for tables and code blocks. Index referencing can handle any number of dimensions. Commas delimit multiple dimensions, as shown below.

  -- https://orgmode.org/manual/Environment-of-a-Code-Block.html
  #+end_quote

#+name: example-table
| id | val |
|----+-----|
|  0 |   3 |
|  1 |   5 |
|  2 |  45 |
|  3 |   1 |
|  4 |  21 |
|  5 |   9 |

When taken from a table and given to =scheme= source blocks, values seem to be lists of integers without converting. (Maybe org-mode has some knowledge built-in?)

#+name: scheme-mean-for-list
#+begin_src scheme :noweb strip-export :results output drawer replace :var x=scheme-data()
(define mean
  (λ (lst)
    (let ([len (length lst)])
      (/ (apply + lst) len))))

(define mean-inexact
  (λ (lst)
    (exact->inexact (mean lst))))

(simple-format
 #t "~a\n"
 (mean-inexact x))
#+end_src

Reference the whole =val= column of the data table:

#+name: mean-table
|   | mean |
|---+------|
| # | 14.0 |
#+TBLFM: @2$2='(org-sbe "scheme-mean-for-list" (x "example-table[,1]"))

* WIP Computing time differences in another programming language

| start                 | end                   | diff |
|-----------------------+-----------------------+------|
| [2023-03-05 So 10:00] | [2023-03-05 So 13:10] |      |
#+TBLFM: @2$2='(org-sbe "scheme-time-diff" (x "example-table[,1]"))
